package com.lantu.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lantu.constant.CommonConstants;
import com.lantu.constant.RedisKeyConstants;
import com.lantu.core.service.IUserService;
import com.lantu.core.entity.User;
import com.lantu.core.mapper.UserMapper;
import com.lantu.dto.user.UserDTO;
import com.lantu.dto.user.UserLoginDTO;
import com.lantu.pojo.user.LoginUser;
import com.lantu.utils.BeanUtils;
import com.lantu.utils.JwtUtils;
import com.lantu.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author binyuhuang
 * @since 2023-02-07
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtUtils jwtUtils;

    @Override
    public LoginUser login(HttpServletRequest request, UserLoginDTO dto) throws Exception {
        // 根据用户名查询
         User user = getUserByUsername(dto.getUsername());
        if (ObjectUtil.isNotEmpty(user) && StrUtil.isNotEmpty(user.getId())) {
            if (user.getDeleted() != 0) {
                throw new Exception("用户不存在");
            }

            if (!Objects.equals(user.getStatus(), CommonConstants.USER_NORMAL_STATUS)) {
                throw new Exception("用户已禁用");
            }

            // 结果不为空，并且密码和传入密码匹配，则生成token，并将用户信息存入redis
            if (passwordEncoder.matches(dto.getPassword(), user.getPassword())) {
                // jwt生成token
                String key = jwtUtils.generateToken(user.getId());

                //更新用户最近登录时间和最近登录ip
                this.updateById(user.updateByLogin(request));

                LoginUser loginUser = LoginUser.newInstance(user, key);

                // 存入redis
                redisUtil.set(RedisKeyConstants.USER_TOKEN + key, loginUser, CommonConstants.DEFAULT_USER_TOKEN_TIMEOUT);

                // 返回数据
                return loginUser;
            }
        }
        return null;
    }


    @Override
    public Map<String, Object> getUserInfo(String token) {
        // 根据token获取用户信息，redis
        Object obj = redisUtil.get(token.startsWith(RedisKeyConstants.USER_TOKEN) ? token : RedisKeyConstants.USER_TOKEN + token);
        if (ObjectUtil.isNotEmpty(obj)) {
            LoginUser loginUser = com.lantu.utils.BeanUtils.toBean(obj, LoginUser.class);
            Map<String, Object> data = new HashMap<>();
            data.put("name", loginUser.getUsername());
            data.put("avatar", loginUser.getAvatar());

            // 角色
            List<String> roleList = this.baseMapper.getRoleNameByUserId(loginUser.getId());
            data.put("roles", roleList);

            return data;
        }
        return null;
    }

    @Override
    public void logout(String token) {
        if(StrUtil.isNotEmpty(token)){
            token = token.startsWith(RedisKeyConstants.USER_TOKEN) ? token : RedisKeyConstants.USER_TOKEN + token;
            if(redisUtil.hasKey(token))redisUtil.delteKey(token);
        }
    }

    @Override
    public void logouts(Set<String> tokens) {
        for(String token : tokens)logout(token);
    }

    @Override
    public User getUserByUsername(String username) {
        if (StrUtil.isNotEmpty(username)) {
            List<User> list = this.lambdaQuery()
                    .eq(User::getUsername, username)
                    .list();
            if (CollUtil.isNotEmpty(list)) {
                return CollUtil.getFirst(list);
            }
        }
        return null;
    }

    @Override
    public boolean checkUserName(String username) {
        boolean flag = false;
        if(StrUtil.isNotEmpty(username)){
            flag = this.lambdaQuery()
                    .eq(User::getUsername, username)
                    .count() > 0;
        }
        return flag;
    }

    @Override
    public boolean checkEmail(String email) {
        boolean flag = false;
        if(StrUtil.isNotEmpty(email)){
            flag = this.lambdaQuery()
                    .eq(User::getEmail, email)
                    .count() > 0;
        }
        return flag;
    }


    @Override
    public Set<String> getTokensByUserId(String userId) {
        Set<String> keys = redisUtil.listCacheKeyByPrefix(RedisKeyConstants.USER_TOKEN);
        Set<String> tokens = new HashSet<>();

        for (String key : keys) {
            if (redisUtil.hasKey(key)) {
                LoginUser loginUser = (LoginUser) redisUtil.get(key);
                if (ObjectUtil.isNotEmpty(loginUser) && loginUser.getId().equals(userId)) {
                    tokens.add(key);
                }
            }
        }
        return tokens;
    }


    @Override
    public List<LoginUser> getLoginUserByUserId(String userId) {
        List<LoginUser> loginUserList = new ArrayList<>();
        Set<String> keys = redisUtil.listCacheKeyByPrefix(RedisKeyConstants.USER_TOKEN);

        for (String key : keys) {
            if (redisUtil.hasKey(key)) {
                LoginUser loginUser = (LoginUser) redisUtil.get(key);
                if (ObjectUtil.isNotEmpty(loginUser) && loginUser.getId().equals(userId)) {
                    loginUserList.add(loginUser);
                }
            }
        }

        return loginUserList;
    }


    @Override
    public List<LoginUser> getALLOnLineUser() {
        List<LoginUser> loginUserList = new ArrayList<>();
        Set<String> keys = redisUtil.listCacheKeyByPrefix(RedisKeyConstants.USER_TOKEN);
        Set<String> addedIds = new HashSet<>();
        for (String key : keys) {
            if (redisUtil.hasKey(key)) {
                LoginUser loginUser = (LoginUser) redisUtil.get(key);
                if (ObjectUtil.isNotEmpty(loginUser) && StrUtil.isNotEmpty(loginUser.getId())) {
                    // 检查是否已经添加了相同id的loginUser
                    if (!addedIds.contains(loginUser.getId())) {
                        loginUserList.add(loginUser);
                        addedIds.add(loginUser.getId()); // 将id添加到已添加的集合中
                    }
                }
            }
        }
        return loginUserList;
    }

    @Override
    public boolean saveUser(User loginUser, UserDTO dto) throws Exception {
        if(ObjectUtil.isNotEmpty(dto) && ObjectUtil.isNotEmpty(loginUser)){
            if(StrUtil.isNotEmpty(dto.getUsername()) && this.checkUserName(dto.getUsername())){
                throw new Exception("用户名已存在");
            }

            if(StrUtil.isNotEmpty(dto.getEmail()) && this.checkEmail(dto.getEmail())){
                throw new Exception("邮箱已存在");
            }

            //修改操作
            if(StrUtil.isNotEmpty(dto.getId())){
                User sourceUser = this.getById(dto.getId());
                if(ObjectUtil.isNotEmpty(sourceUser) && StrUtil.isNotEmpty(sourceUser.getId())){
                    sourceUser = BeanUtils.toBean(dto, User.class, "password");
                    sourceUser.updateBy(loginUser.getUsername());
                    return this.updateById(sourceUser);
                }
            }
            //新增操作
            else{
                User newUser = BeanUtils.toBean(dto, User.class, "password");
                newUser.setPassword(passwordEncoder.encode(dto.getPassword()));
                newUser.createBy(loginUser.getUsername());
                return this.save(newUser);
            }
        }
        return false;
    }
}
